<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>D3: Hide and reveal vehicles</title>
		<script type="text/javascript" src="../d3.js"></script>
		<style type="text/css">

			h1 {
				font-family: Helvetica, sans-serif;
				font-size: 14px;
				font-weight: bold;
			}

			.area {
				stroke: none;
				cursor: pointer;
			}

			.area:hover {
				fill: yellow;
			}
			
			.area.unclickable {
				pointer-events: none;
			}

		</style>
	</head>
	<body>
		<h1>Monthly Number of Electric-Drive Vehicles Sold in the U.S. (by Type): January 2005&ndash;February 2017</h1>
		<script type="text/javascript">

			//Width and height
			var w = 1400;
			var h = 300;
			var padding = 20;
			
			var dataset, xScale, yScale, xAxis, yAxis, area;  //Empty, for now

			//For converting strings to Dates
			var parseTime = d3.timeParse("%Y-%m");

			//For converting Dates to strings
			var formatTime = d3.timeFormat("%b %Y");
			
			//Define key function, to be used when binding data
			var key = function(d) {
				return d.key;
			};

			//Set up stack methods
			var vehicleStack = d3.stack();
			var typeStack = d3.stack();

			//Load in data
			d3.request("vehicle_sales_data.csv")
				.mimeType("text/csv")
				.get(function(response) {



					//
					// DATA PARSING
					//

					//Parse each row of the CSV into an array of string values
					var rows = d3.csvParseRows(response.responseText);
					// console.log(rows);
					
					//Make dataset an empty array, so we can start adding values
					dataset = [];
					
					//Loop once for each row of the CSV, starting at row 3,
					//since rows 0-2 contain only vehicle info, not sales values.
					for (var i = 3; i < rows.length; i++) {
						
						//Create a new object
						dataset[i - 3] = {
							date: parseTime(rows[i][0])  //Make a new Date object for each year + month
						};
						
						//Loop once for each vehicle in this row (i.e., for this date)
						for (var j = 1; j < rows[i].length; j++) {
						
							var make		= rows[0][j];  //'Make' from 1st row in CSV
							var model		= rows[1][j];  //'Model' from 2nd row in CSV
							var makeModel	= rows[0][j] + " " + rows[1][j];  //'Make' + 'Model' will serve as our key
							var type 		= rows[2][j];  //'Type' from 3rd row in CSV
							var sales		= rows[i][j];  //Sales value for this vehicle and month
							
							//If sales value exists…
							if (sales) {
								sales = parseInt(sales);  //Convert from string to int
							} else {  //Otherwise…
								sales = 0;  //Set to zero
							}

							//Append a new object with data for this vehicle and month
							dataset[i - 3][makeModel] = {
								"make": make,
								"model": model,
								"type": type,
								"sales": sales
							};
							
						}

					}

					//Log out the final state of dataset
					// console.log(dataset);



					//
					// STACKING (removed here, now happens only after click…)
					//
					
					
					
					//
					//	TYPE DATA SERIES
					//
					
					//The goal here is to make a totally separate data set that
					//includes just monthly totals for each `type` (HEV, PHEV, BEV, FCEV).
					
					//Make typeDataset an empty array, so we can start adding values
					typeDataset = [];
					
					//Loop once for each row of the CSV, starting at row 3,
					//since rows 0-2 contain only vehicle info, not sales values.
					for (var i = 3; i < rows.length; i++) {
						
						//Create a new object
						typeDataset[i - 3] = {
							date: parseTime(rows[i][0]),  //Make a new Date object for each year + month
							"HEV": 0,
							"PHEV": 0,
							"BEV": 0,
							"FCEV": 0
						};
						
						//Loop once for each vehicle in this row (i.e., for this date)
						for (var j = 1; j < rows[i].length; j++) {
						
							var type 		= rows[2][j];  //'Type' from 3rd row in CSV
							var sales		= rows[i][j];  //Sales value for this vehicle and month
							
							//If sales value exists…
							if (sales) {
								sales = parseInt(sales);  //Convert from string to int
							} else {  //Otherwise…
								sales = 0;  //Set to zero
							}

							//Add sales value to existing sum
							typeDataset[i - 3][type] += sales;
							
						}

					}

					//Log out the final state of dataset
					// console.log(typeDataset);



					//
					// STACKING
					//

					//Tell stack function where to find the keys
					var types = [ "HEV", "PHEV", "BEV", "FCEV" ];
					typeStack.keys(types);

					//Stack the data and log it out
					var typeSeries = typeStack(typeDataset);
					// console.log(typeSeries);



					//
					// MAKE THE CHART
					//
				
					//Create scale functions
					xScale = d3.scaleTime()
							   .domain([
									d3.min(dataset, function(d) { return d.date; }),
									d3.max(dataset, function(d) { return d.date; })
								])
							   .range([padding, w - padding * 2]);
				
					yScale = d3.scaleLinear()
								.domain([
									0,
									d3.max(typeDataset, function(d) {
										var sum = 0;

										//Loops once for each row, to calculate
										//the total (sum) of sales of all vehicles
										for (var i = 0; i < types.length; i++) {
											sum += d[types[i]];
										};
			
										return sum;
									})
								])
								.range([h - padding, padding / 2])
								.nice();
				
					//Define axes
					xAxis = d3.axisBottom()
							   .scale(xScale)
							   .ticks(10)
							   .tickFormat(formatTime);
				
					//Define Y axis
					yAxis = d3.axisRight()
							   .scale(yScale)
							   .ticks(5);
				
					//Define area generator
					area = d3.area()
								.x(function(d) { return xScale(d.data.date); })
								.y0(function(d) { return yScale(d[0]); })
								.y1(function(d) { return yScale(d[1]); });
				
					//Create SVG element
					var svg = d3.select("body")
								.append("svg")
								.attr("width", w)
								.attr("height", h);
				
					//Create areas for individual VEHICLES
					//
					//(Removed here, now happens only after click…)
					//
					svg.append("g")
						.attr("id", "vehicles");
						
					//Create areas for TYPES
					svg.append("g")
						.attr("id", "types")
						.selectAll("path")
						.data(typeSeries, key)
						.enter()
						.append("path")
						.attr("class", "area")
						.attr("opacity", 1)
						.attr("d", area)
						.attr("fill", function(d) {

							//Which type is this?
							var thisType = d.key;
							
							//New color var
							var color;

							switch (thisType) {
								case "HEV":
									color = "rgb(110, 64, 170)";
									break;
								case "PHEV":
									color = "rgb(76, 110, 219)";
									break;
								case "BEV":
									color = "rgb(35, 171, 216)";
									break;
								case "FCEV":
									color = "rgb(29, 223, 163)";
									break;
							}
							
							return color;
						})
						.on("click", function(d) {
							
							//
							// TYPES
							//
							
							//Which type was clicked?
							var thisType = d.key;
							
							//Generate a new data set with all-zero values, 
							//except for this type's data
							var thisTypeDataset = [];
							
							for (var i = 0; i < typeDataset.length; i++) {
								thisTypeDataset[i] = {
									date: 		typeDataset[i].date,
									HEV:		0,
									PHEV:		0,
									BEV:		0,
									FCEV:		0,
									[thisType]:	typeDataset[i][thisType]  //Overwrites the appropriate zero value above
								}
							}
							
							// console.log(thisTypeDataset);
							
							//Stack the data (even though there's now just one "layer") and log it out
							var thisTypeSeries = typeStack(thisTypeDataset);
							// console.log(thisTypeSeries);
							
							//Bind the new data set to paths, overwriting old bound data.
							var paths = d3.selectAll("#types path")
								.data(thisTypeSeries, key)
								.classed("unclickable", "true");

							//Transition areas into new positions (i.e., thisType's area
							//will go to a zero baseline; all others will flatten out).
							//
							//Store this transition in a new variable for later reference.
							var areaTransitions = paths.transition()
								.duration(1000)
								.attr("d", area);
								
							//Update scale
							yScale.domain([
									0,
									d3.max(thisTypeDataset, function(d) {
										var sum = 0;
			
										//Calculate the total (sum) of sales of this type,
										//ignoring the others (for now)
										sum += d[thisType];
										
										return sum;
									})
								]);

							//Append this transition to the one already in progress
							//(from above).  Transition areas to newly updated scale.
							areaTransitions.transition()
								.delay(200)
								.on("start", function() {

									//Transition axis to new scale concurrently
									d3.select("g.axis.y")
										.transition()
										.duration(1000)
										.call(yAxis);
											
								})
								.duration(1000)
								.attr("d", area)
								.transition()
								.on("start", function() {
									//Make vehicles visible instantly, so 
									//they are revealed when this fades out
									d3.selectAll("g#vehicles path")
										.attr("opacity", 1);
								})
								.duration(1000)
								.attr("opacity", 0);

							//
							// VEHICLES
							//

							//Get all possible keys (make + model), but toss out 'date'
							var keysAll = Object.keys(dataset[0]).slice(1);
							// console.log(keysAll);
							
							//Loop once for each key, and save out just the ones of thisType (e.g. BEVs)
							var keysOfThisType = [];
							for (var i = 0; i < keysAll.length; i++) {
								if (dataset[0][keysAll[i]].type == thisType) {
									keysOfThisType.push(keysAll[i]);
								}
							}
							// console.log(keysOfThisType);
							
							//Give the new keys to the stack function
							vehicleStack.keys(keysOfThisType)
								.value(function value(d, key) {
									return d[key].sales;
								});

							//Stack the data and log it out
							var vehicleSeries = vehicleStack(dataset);
							// console.log(vehicleSeries);

							//Create areas for individual VEHICLES
							svg.select("g#vehicles")
								.selectAll("path")
								.data(vehicleSeries, key)
								.enter()
								.append("path")
								.attr("class", "area")
								.attr("opacity", 0)
								.attr("d", area)
								.attr("fill", function(d, i) {

									//Which vehicle is this?
									var thisKey = d.key;
									
									//What 'type' is this vehicle?
									var thisType = d[0].data[thisKey].type;
									// console.log(thisType);

									//Used to find a cool color below
									var spread = 0.2;
									var startingPoint;

									//Choose where in the color spectrum we start, based on type
									switch (thisType) {
										case "HEV":
											startingPoint = 0;
											break;
										case "PHEV":
											startingPoint = 0.2;
											break;
										case "BEV":
											startingPoint = 0.4;
											break;
										case "FCEV":
											startingPoint = 0.6;
											break;
									}

									//How many cars?
									var numVehicles = keysOfThisType.length;
									
									//Get a value between 0.0 and 1.0
									var normalized = startingPoint + ((i / numVehicles) * spread);

									//Get that color on the spectrum
									return d3.interpolateCool(normalized);
								})
								.append("title")  //Make tooltip
								.text(function(d) {
									return d.key;
								});					

						})
						.append("title")  //Make tooltip
						.text(function(d) {
							return d.key;
						});
				
					//Create axes
					svg.append("g")
						.attr("class", "axis x")
						.attr("transform", "translate(0," + (h - padding) + ")")
						.call(xAxis);
				
					svg.append("g")
						.attr("class", "axis y")
						.attr("transform", "translate(" + (w - padding * 2) + ",0)")
						.call(yAxis);



				});
			
		</script>
	</body>
</html>